﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Threading;
using System.Data.SqlClient;
using System.Collections.ObjectModel;
using KegData;
using KegCommunication;

namespace KegManager
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Receiver pipe = new Receiver();
        private KegeratorDataContext monitor;
        int SelectedTabIndex = -1;

        public TimeSpan TimeoutToHide { get; private set; }
        public DateTime LastMouseMove { get; private set; }
        public bool IsHidden { get; private set; }
        public DispatcherTimer MouseTimer { get; private set; }

        // TODO: Have a different pour window for each tap, centered on each tap.
        PourWindow PourWin = new PourWindow();

        private ObservableCollection<TemperatureDisplay> recenttemperatures;
        public ObservableCollection<TemperatureDisplay> RecentTemperatures
        {
            get
            {
                if (recenttemperatures == null)
                {
                    return new ObservableCollection<TemperatureDisplay>();
                }
                return recenttemperatures;
            }
        }

        private ObservableCollection<Tap> activeTaps;
        public ObservableCollection<Tap> ActiveTaps
        {
            get
            {
                if (activeTaps == null)
                {
                    return new ObservableCollection<Tap>();
                }
                return activeTaps;
            }
        }

        public MainWindow()
        {
            // Autohide the mouse cursor after 5 seconds
            TimeoutToHide = TimeSpan.FromSeconds(5);
            MouseTimer = new DispatcherTimer();
            MouseTimer.Tick += new EventHandler(MouseTimer_Tick);
            MouseTimer.Interval = TimeSpan.FromSeconds(2);
            MouseTimer.Start();

            monitor = App.OpenDataSource(this.ConnectionString);
            recenttemperatures = new ObservableCollection<TemperatureDisplay>();
            activeTaps = new ObservableCollection<Tap>();

            // Initialize the temperature locations
            var locations = (from l in monitor.TemperatureLocations
                             select l);
            foreach (TemperatureLocation tl in locations)
            {
                TemperatureDisplay td = new TemperatureDisplay(tl);

                // Get the latest value from the location
                Temperature latestTemp = (from t in monitor.Temperatures
                                          orderby t.Time descending
                                          where t.TemperatureLocation == tl
                                          select t).FirstOrDefault();

                td.Update(latestTemp);
                recenttemperatures.Add(td);
            }

            // Add all taps, even the blank ones
            int numberOfTaps = Properties.Settings.Default.TapCount;
            for (int i = 0; i < numberOfTaps; i++)
            {
                Tap tap = (from t in monitor.Taps
                           where t.Position == i
                           select t).FirstOrDefault();
                activeTaps.Add(tap);
            }
            InitializeComponent();
        }

        void DataBeingRecieved(List<string> data)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate() { MessageBox.Show("woo", "woo", MessageBoxButton.OK); }
             ); 
        }

        void OnUpdateTemperature(TemperatureEventArgs te)
        {
            // Update the temperature objects
            foreach(TemperatureDisplay td in recenttemperatures)
            {
                if (td.OneWireId == te.Address)
                {
                    td.Value = te.Temperature;
                }
            }
        }

        public string ConnectionString
        {
            get
            {
                return String.Format("Data Source={0};Initial Catalog={1};Persist Security Info=True;User ID={2};Password={3};Pooling=False",
                                        Properties.Settings.Default.dbServer,
                                        Properties.Settings.Default.dbDatabase,
                                        Properties.Settings.Default.dbUser,
                                        Properties.Settings.Default.dbPassword);
            }

        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            pipe.StartPour += new KegCommunication.Communication.PipeService.StartPour(OnStartPour);
            pipe.UserAuthenticated += new KegCommunication.Communication.PipeService.UserAuthenicated(OnUserAuthenticated);
            pipe.UpdateTemperature += new KegCommunication.Communication.PipeService.UpdateTemperature(OnUpdateTemperature);

            // This error will occur if instance of the kegmanager is already using the pipe
            if (pipe.ServiceOn() == false)
            {
                MessageBox.Show(pipe.error.Message);
            }
        }

        private void OnUserAuthenticated(UserAuthenticatedEventArgs ue)
        {
            User user = (from u in monitor.Users
                         where u.Id == ue.Id
                         select u).FirstOrDefault();
            PourWin.OnUserAuthenticated(user);
        }

        private void OnStartPour(PourEventArgs pe)
        {
            PourWin.SetTap(activeTaps[pe.Tap]);
            pipe.ContinuePour += new KegCommunication.Communication.PipeService.ContinuePour(PourWin.OnContinuePour);
            pipe.EndPour += new KegCommunication.Communication.PipeService.EndPour(PourWin.OnEndPour);

            //PourWin.Show();
        }

        private void OnEndPour(PourEventArgs pe)
        {
            // Remove the pour window events as the window has been closed.
            pipe.ContinuePour -= new KegCommunication.Communication.PipeService.ContinuePour(PourWin.OnContinuePour);
            pipe.EndPour -= new KegCommunication.Communication.PipeService.EndPour(PourWin.OnEndPour);
        }

        private void LoadTabData()
        {
            TabItem ti = MainTab.SelectedItem as TabItem;
            if (String.Compare(ti.Header.ToString(), "Taps") == 0)
            {
            }
            else if (ti.Header.ToString() == "Kegs")
            {
                var kegs = (from k in monitor.Kegs
                            select k);
                listKegs.ItemsSource = kegs;

                // Sort the kegs by volume
                ICollectionView view = CollectionViewSource.GetDefaultView(listKegs.ItemsSource);
                view.SortDescriptions.Clear();
                view.SortDescriptions.Add(new SortDescription("CurrentVolume", ListSortDirection.Descending));  
            }
            else if (ti.Header.ToString() == "Beers")
            {
                var beers = (from b in monitor.Beers
                            select b);
                listBeers.ItemsSource = beers;
            }
            else if (ti.Header.ToString() == "Pours")
            {
                var pours = (from p in monitor.Pours
                             select p);
                listPours.ItemsSource = pours;

                // Sort the pours by date
                ICollectionView view = CollectionViewSource.GetDefaultView(listPours.ItemsSource);
                view.SortDescriptions.Clear();
                view.SortDescriptions.Add(new SortDescription("Time", ListSortDirection.Descending));
            }
            else if (ti.Header.ToString() == "Temperatures")
            {
                
            }
            else if (ti.Header.ToString() == "Users")
            {
                var users = (from u in monitor.Users
                             select u);
                listUsers.ItemsSource = users;
            }
            else if (ti.Header.ToString() == "Control")
            {
                // TODO: Databind these to the user properties instead
                txtServer.Text = Properties.Settings.Default.dbServer;
                txtDatabase.Text = Properties.Settings.Default.dbDatabase;
                txtUser.Text = Properties.Settings.Default.dbUser;
                txtPassword.Text = Properties.Settings.Default.dbPassword;

                txtDataDirectory.Text = Properties.Settings.Default.DataDirectory;

                // Select the appropriate values into the units comboboxes
                foreach (ComboBoxItem i in cmbTemperatureUnit.Items)
                {
                    if (String.Compare(i.Content.ToString(), Properties.Settings.Default.TemperatureUnit, true) == 0)
                    {
                        cmbTemperatureUnit.SelectedItem = i;
                        break;
                    }
                }

                foreach (ComboBoxItem i in cmbVolumeUnit.Items)
                {
                    if (String.Compare(i.Content.ToString(), Properties.Settings.Default.VolumeUnit, true) == 0)
                    {
                        cmbVolumeUnit.SelectedItem = i;
                        break;
                    }
                }
                
            }
            else if (ti.Header.ToString() == "Test")
            {

            }
        }

        private void MainTab_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Load the tab data only if a new tab is selected.
            if (MainTab.SelectedIndex != SelectedTabIndex)
            {
                SelectedTabIndex = MainTab.SelectedIndex;
                LoadTabData();
            }
        }

        private void Window_StateChanged(object sender, EventArgs e)
        {
            // If the window is being maximized, it should be made fullscreen.
            if (this.WindowState == WindowState.Maximized)
            {
                // Remove the border to make it fullscreen.
                this.WindowStyle = WindowStyle.None;
            }
            if (this.WindowState == WindowState.Normal)
            {
                this.WindowStyle = WindowStyle.SingleBorderWindow;
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // ToggleFullscreen
            if (this.WindowState == WindowState.Maximized)
            {
                this.WindowState = WindowState.Normal;
                this.WindowStyle = WindowStyle.SingleBorderWindow;
            }
            else
            {
                this.WindowState = WindowState.Maximized;
                this.WindowStyle = WindowStyle.None;
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            // Load a new keg into the keg window

            KegWindow kwin = new KegWindow(monitor);

            if (kwin.ShowDialog() == true)
            {
                // Force a reload of the data in the list box
                LoadTabData();
            }
        }

        private void buttonModifyKeg_Click(object sender, RoutedEventArgs e)
        {
            Keg selectedKeg = listKegs.SelectedItem as Keg;
            KegWindow kwin = new KegWindow(monitor, selectedKeg);
            kwin.ShowDialog();
            
        }

        private void listKegs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            bool enable = false;

            if (listKegs.SelectedItem != null)
            {
                enable = true;
            }
            else
            {
                enable = false;
            }

            buttonModifyKeg.IsEnabled = enable;
            buttonDeleteKeg.IsEnabled = enable;
            buttonSetTap1.IsEnabled = enable;
            buttonSetTap2.IsEnabled = enable;
        }

        private void buttonSetTap1_Click(object sender, RoutedEventArgs e)
        {
            SetKegOnTap(0);
        }

        private void buttonSetTap2_Click(object sender, RoutedEventArgs e)
        {
            SetKegOnTap(1);
        }

        private void SetKegOnTap(byte Position)
        {
            // Get the currently selected keg
            Keg currentKeg = listKegs.SelectedItem as Keg;

            if (currentKeg != null)
            {
                Tap tap = (from t in monitor.Taps
                         where t.Position == Position
                         select t).FirstOrDefault();
                if (tap == null)
                {
                    tap = new Tap();
                    tap.Position = Position;
                    monitor.Taps.InsertOnSubmit(tap);
                }

                tap.Keg = currentKeg;
                monitor.SubmitChanges();
            }
        }

        private void buttonDeleteKeg_Click(object sender, RoutedEventArgs e)
        {
            // Delete the selected keg
            // TODO: Do I also want to delete any pours referencing this keg?  The Pours will be useless without it.
            Keg selectedKeg = listKegs.SelectedItem as Keg;

            if (selectedKeg != null)
            {
                if (MessageBox.Show("Are you sure you want to delete this keg of \"" + selectedKeg.Beer.Name + "\"?", "Confirm Delete", MessageBoxButton.YesNo) ==
                    MessageBoxResult.Yes)
                {
                    monitor.Kegs.DeleteOnSubmit(selectedKeg);
                    monitor.Pours.DeleteAllOnSubmit(selectedKeg.Pours);
                    monitor.SubmitChanges();

                    // Force a refresh of the keg list
                    LoadTabData();
                }
            }
        }

        private void buttonNewBeer_Click(object sender, RoutedEventArgs e)
        {
            // Load a new keg into the keg window

            BeerWindow bwin = new BeerWindow(monitor);

            if (bwin.ShowDialog() == true)
            {
                // Force a reload of the data in the list box
                LoadTabData();
            }
        }

        private void buttonModifyBeer_Click(object sender, RoutedEventArgs e)
        {
            Beer selectedBeer = listBeers.SelectedItem as Beer;

            BeerWindow bwin = new BeerWindow(monitor, selectedBeer);
            bwin.ShowDialog();
        }

        private void buttonDeleteBeer_Click(object sender, RoutedEventArgs e)
        {
            // Delete the selected keg
            Beer selectedBeer = listKegs.SelectedItem as Beer;

            if (selectedBeer != null)
            {
                if (MessageBox.Show("Are you sure you want to delete \"" + selectedBeer.Name + "\"?", "Confirm Delete", MessageBoxButton.YesNo) ==
                    MessageBoxResult.Yes)
                {
                    monitor.Beers.DeleteOnSubmit(selectedBeer);
                    monitor.SubmitChanges();

                    // Force a refresh of the keg list
                    LoadTabData();
                }
            }
        }

        private void listBeers_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            bool enable = false;

            if (listBeers.SelectedItem != null)
            {
                enable = true;
            }
            else
            {
                enable = false;
            }

            buttonModifyBeer.IsEnabled = enable;
            buttonDeleteBeer.IsEnabled = enable;
        }

        private void Button_TestDatabase(object sender, RoutedEventArgs e)
        {
            SqlConnection myConnection = new SqlConnection(String.Format(
                                       "user id={0};" +
                                       "password={1};" +
                                       "server={2};" +
                                       "database={3}; " +
                                       "connection timeout=30",
                                       txtUser.Text,
                                       txtPassword.Text,
                                       txtServer.Text,
                                       txtDatabase.Text));
            try
            {
                myConnection.Open();
                MessageBox.Show("Successfully connected to database", "Success!");
                myConnection.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error connecting to database");
            }
        }

        private void Button_ApplyDatabase(object sender, RoutedEventArgs e)
        {
            Properties.Settings.Default.dbServer = txtServer.Text;
            Properties.Settings.Default.dbDatabase = txtDatabase.Text;
            Properties.Settings.Default.dbUser = txtUser.Text;
            Properties.Settings.Default.dbPassword = txtPassword.Text;

            Properties.Settings.Default.Save();
            
            // Reload the datasource
            monitor = App.OpenDataSource(this.ConnectionString);

            // TODO: What can I do to ensure that the monitor and manager are working off of the same database?
            // Otherwise, the monitor will send pours to the manager, but they will not be reflected in the display.
            // I guess I can probably just leave the responsibility to the user.
        }

        private void Button_RevertDatabase(object sender, RoutedEventArgs e)
        {
            // TODO: these should be databound and I should call Reset() instead
            txtServer.Text = Properties.Settings.Default.dbServer;
            txtDatabase.Text = Properties.Settings.Default.dbDatabase;
            txtUser.Text = Properties.Settings.Default.dbUser;
            txtPassword.Text = Properties.Settings.Default.dbPassword;
        }

        private void ApplyDataDirectory_Click(object sender, RoutedEventArgs e)
        {
            // Ask to move exisitng files
            MessageBoxResult result = MessageBox.Show("This will move all existing files to the new location.  Are you sure you would like to do this?", "Set Data Directory", MessageBoxButton.YesNo, MessageBoxImage.Question);

            if (result == MessageBoxResult.Yes)
            {
                
                string oldLocation = Properties.Settings.Default.DataDirectory;
                string newLocation = txtDataDirectory.Text;

                // Save the setting
                Properties.Settings.Default.DataDirectory = txtDataDirectory.Text;
                Properties.Settings.Default.Save();

                System.IO.Directory.Move(oldLocation, newLocation);
            }
        }

        private void ChangeUnits(object sender, SelectionChangedEventArgs e)
        {
            ComboBox cmb = sender as ComboBox;
            ComboBoxItem item = cmb.SelectedItem as ComboBoxItem;
            
            if (cmb != null && item != null)
            {
                if (cmb == cmbTemperatureUnit)
                {
                    Properties.Settings.Default.TemperatureUnit = item.Content.ToString();
                }
                else if (cmb == cmbVolumeUnit)
                {
                    Properties.Settings.Default.VolumeUnit = item.Content.ToString();
                }

                Properties.Settings.Default.Save();
            }
        }

        private void mainWindow_Closing(object sender, CancelEventArgs e)
        {

        }

        private void listUsers_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            UserDisplay.DataContext = listUsers.SelectedItem as User;

            // Sort the pours by date
            ICollectionView view = CollectionViewSource.GetDefaultView(listUserPours.ItemsSource);
            view.SortDescriptions.Clear();
            view.SortDescriptions.Add(new SortDescription("Time", ListSortDirection.Descending));

            UserDisplay.Visibility = Visibility.Visible;
        }

        private void mainWindow_MouseMove(object sender, MouseEventArgs e)
        {
            LastMouseMove = DateTime.Now;
            if (IsHidden) {
                Cursor = Cursors.Arrow;
                IsHidden = false; 
            }
        }

        private void MouseTimer_Tick(object sender, EventArgs e)
        {
            TimeSpan elapsed = DateTime.Now - LastMouseMove;
            if (elapsed >= TimeoutToHide && !IsHidden)
            { 
                Cursor = Cursors.None;
                IsHidden = true;
            }
        }
    }
}

public static class ConsoleEx
{

    /// <summary>
    /// Shows the Console
    /// </summary>
    [DllImport("kernel32.dll")]
    public static extern Boolean AllocConsole();
    /// <summary>
    /// Hides the Console
    /// </summary>
    [DllImport("kernel32.dll")]
    public static extern Boolean FreeConsole();

}